using System;
using System.Linq;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
using System.Collections.Generic;
using Lucene.Net.Search;
using Speed;
using SpeedSearch.FullText;
using Lucene.Net.Documents;
using Lucene.Net.Analysis;
using Lucene.Net.Store;
using Lucene.Net.QueryParsers;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;

namespace SpeedSearch.FullText
{

    public class DocSearcher2 : IDisposable
    {

        string directory;

        IndexSearcher indexSearcher;
        Analyzer analyzer;
        Lucene.Net.Store.Directory dirIndex;
        readonly static SimpleFSLockFactory _LockFactory = new SimpleFSLockFactory();

        public DocSearcher2(string directory)
        {
            if (System.IO.Directory.GetFiles(directory).Length == 0)
                throw new Exception("O diretório '" + directory + "' não está indexado. É necessário realizar uma reindexação");

            this.directory = directory;
            // dirIndex = FSDirectory.Open(new DirectoryInfo(directory), _LockFactory);
            dirIndex = FSDirectory.Open(directory);
            indexSearcher = new IndexSearcher(dirIndex, true);
            analyzer = new StandardAnalyzer(Program.LVersion);
        }

        public void Dispose()
        {
            if (dirIndex != null)
            {
                dirIndex.Dispose();
                dirIndex = null;
            }
            if (indexSearcher != null)
            {
                indexSearcher.Dispose();
                indexSearcher = null;
            }
            if (analyzer != null)
            {
                analyzer.Dispose();
                analyzer = null;
            }
        }

        Query lastquery;
        public List<DocInfo> FullTextSearch(string contains, string notContains, string field, EnumSearchMode mode, string extensions, bool incremental, int maxDocs = Int32.MaxValue)
        {
            Query qText = QueryParser(contains, field, mode);
            Query qTextNot = null;
            if (!string.IsNullOrWhiteSpace(notContains))
                qTextNot = QueryParser(notContains, "-" + field, mode);

            BooleanQuery _query = new BooleanQuery();
            
            _query.Add(qText, Occur.MUST);
            if (qTextNot != null)
                _query.Add(qTextNot, Occur.MUST);

            if (!string.IsNullOrWhiteSpace(extensions))
            {
                string exts = extensions
                    .Trim()
                    .Replace("*", "")
                    .Replace(".", "")
                    .Replace(",", ";")
                    .Replace(" ", "")
                    .Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
                    .Concat(" and ");

                Query qExts = new TermQuery(new Term("ext", exts));
                _query.Add(qExts, Occur.MUST);
            }

            if (incremental)
            {
                BooleanQuery bq = new BooleanQuery();
                bq.Add(_query, Occur.MUST);
                bq.Add(lastquery, Occur.MUST);
                _query = bq;
            }

            lastquery = _query;

            TopDocs topDocs = indexSearcher.Search(_query, maxDocs);
            var hits = topDocs.ScoreDocs;
            List<DocInfo> docs = new List<DocInfo>();
            for (int i = 0; i < hits.Length; i++)
            {
                DocInfo doc = new DocInfo();
                doc.Document = indexSearcher.Doc(hits[i].Doc);
                doc.Score = hits[i].Score;
                docs.Add(doc);
            }
            return docs;
        }

        public Query QueryParser(string sql, string field, EnumSearchMode mode)
        {
            var queryParser = new QueryParser(Program.LVersion, field, analyzer);

            if (mode == EnumSearchMode.And)
                queryParser.DefaultOperator = Lucene.Net.QueryParsers.QueryParser.Operator.AND;
            else if (mode == EnumSearchMode.Or)
                queryParser.DefaultOperator = Lucene.Net.QueryParsers.QueryParser.Operator.OR;

            var _query = queryParser.Parse(sql);
            return _query;
        }

        public List<DocFile> Search(string field, string expression)
        {
            return Search(null, null, field, expression, EnumSearchMode.And, false, null, false);
        }

        public List<DocFile> Search(string textContains, string textNotContains, string field, string expression, EnumSearchMode searchMode, bool getOnlyFullName, string extensions, bool incremental)
        {
            List<DocFile> list = new List<DocFile>();
            string queryCon = ParseQuery(textContains, field, expression, searchMode);
            string queryNot = !string.IsNullOrWhiteSpace(textNotContains) ? ParseQuery(textContains, field, expression, searchMode) : null;
            List<DocInfo> docs = FullTextSearch(queryCon, queryNot, field, searchMode, extensions, incremental, Int32.MaxValue);
            var dirs = Program.Db.Directories.ToDictionary(p => p.DirectoryId);

            foreach (DocInfo docInfo in docs)
            {
                DocFile file = new DocFile();
                Document doc = docInfo.Document;

                file.FullName = doc.GetField("fname").StringValue;

                if (!getOnlyFullName)
                {
                    file.Document = doc;
                    file.IndexId = Conv.ToInt32(doc.GetField("id").StringValue);
                    file.DirId = Conv.ToInt32(doc.GetField("dir").StringValue);
                    file.DirName = dirs[file.DirId].DirName;
                    file.FileName = Path.GetFileName(file.FullName); //  doc.GetField("name").StringValue;
                    file.Extension = Path.GetExtension(file.FileName); //  doc.GetField("ext").StringValue;
                    file.FileSize = Convert.ToInt32(doc.GetField("size").StringValue);
                    file.DateCreated = DateTools.StringToDate(doc.GetField("create").StringValue);
                    file.DateModified = DateTools.StringToDate(doc.GetField("modif").StringValue);
                    file.Score = docInfo.Score * 100;
                    //file.Tag = "Página " + file.PageNumber;
                }

                list.Add(file);
            }

            list.Sort(compareByScore);
            return list;
        }

        /*
        public List<ScoreDoc> Search(string sql, string field)
        {
            List<DocFile> list = new List<DocFile>();
            TopDocs hits = FullTextSearch(sql, field);

            List<ScoreDoc> docs = new List<ScoreDoc>();
            for (int i = 0; i < hits.ScoreDocs.Length; i++)
                docs.Add(hits.ScoreDocs[i]);
            return docs;
        }
        */

        private static int compareByScore(DocFile s1, DocFile s2)
        {
            if (s1.Score > s2.Score)
                return -1;
            else
                return 1;
        }

        public string ParseQuery(string textRequired, string field, string text, EnumSearchMode searchMode)
        {
            string query = "";
            switch (searchMode)
            {
                case EnumSearchMode.Boolean:
                    query = Speed.StringUtil.Concat(ParseWords(field, text, true).ToArray(), " ");
                    break;

                case EnumSearchMode.And:
                    query = Speed.StringUtil.Concat(ParseWords(field, text, true).ToArray(), " AND ");
                    break;

                case EnumSearchMode.Or:
                    query = Speed.StringUtil.Concat(ParseWords(field, text, true).ToArray(), " OR ");
                    break;

                case EnumSearchMode.Exact:
                    query = text.Trim();
                    if (query.IndexOf("\"") == -1)
                    {
                        if (!query.StartsWith("\""))
                            query = "\"" + query;
                        if (!query.EndsWith("\""))
                            query = query + "\"";
                    }
                    break;
            }

            if (!string.IsNullOrEmpty(textRequired))
                query = string.Format("({0}) AND ({1})", textRequired, query);
            //else
            //    query = string.Format("{0}: {1}", field, query);

            return query;

        }

        public List<string> ParseWords(string field, string text, bool removeOperators)
        {
            //return new List<string>(new string[] { text });

            string[] results = System.Text.RegularExpressions.Regex.Split(text, "\"");
            List<string> list = new List<string>();
            foreach (string result in results)
            {
                if (result.Trim().Length > 0)
                {
                    string withAspas = "\"" + result + "\"";
                    if ((text.IndexOf(withAspas) > -1))
                        list.Add(field + ":" + withAspas.Trim());
                    else
                    {
                        string[] values = result.Split(' ');
                        foreach (string value in values)
                        {
                            string val = value.Trim().ToLower();
                            if (val.Length > 0)
                            {
                                if (val != "and" && val != "or")
                                    list.Add(field + ":" + value.Trim());
                            }
                        }
                    }
                }
            }
            return list;
        }

    }

}
